/**
 * Copyright (C) 2018-2022
 * All rights reserved, Designed By www.yixiang.co
 */
package co.yixiang.modules.security.rest;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.RSA;
import co.yixiang.annotation.AnonymousAccess;
import co.yixiang.exception.BadRequestException;
import co.yixiang.modules.logging.aop.log.Log;
import co.yixiang.modules.security.config.SecurityProperties;
import co.yixiang.modules.security.security.TokenUtil;
import co.yixiang.modules.security.security.provider.UserAuthenticationProvider;
import co.yixiang.modules.security.security.vo.*;
import co.yixiang.modules.security.service.AppOnlineUserService;
import co.yixiang.modules.security.service.UserLoginService;
import co.yixiang.modules.user.domain.YxUser;
import co.yixiang.modules.user.service.YxUserService;
import co.yixiang.utils.RedisUtils;
import co.yixiang.utils.SecurityUtils;
import co.yixiang.utils.StringUtils;
import com.wf.captcha.ArithmeticCaptcha;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author liu
 * @date 2023-12-23
 * APP授权、根据token获取用户详细信息
 */
@Slf4j
@RestController
@RequestMapping("/auth/app")
@Api(tags = "系统：系统授权接口-app")
public class AuthAppController {

    @Value("${loginCode.expiration}")
    private Long expiration;
    @Value("${loginSmsCode.expiration}")
    private Long smsExpiration;
    @Value("${rsa.private_key}")
    private String privateKey;
    @Value("${single.login}")
    private Boolean singleLogin;
    private final SecurityProperties properties;
    private final RedisUtils redisUtils;
    private final UserDetailsService userDetailsService;
    private final AppOnlineUserService onlineUserService;
    private final TokenUtil tokenUtil;
    private final UserAuthenticationProvider authenticationManagerBuilder;
    private final YxUserService yxUserService;
    private final PasswordEncoder passwordEncoder;

    private final UserLoginService userLoginService;

    public AuthAppController(SecurityProperties properties, RedisUtils redisUtils, UserDetailsService userDetailsService, AppOnlineUserService onlineUserService, TokenUtil tokenUtil, UserAuthenticationProvider authenticationManagerBuilder, YxUserService yxUserService, PasswordEncoder passwordEncoder, UserLoginService userLoginService) {
        this.properties = properties;
        this.redisUtils = redisUtils;
        this.userDetailsService = userDetailsService;
        this.onlineUserService = onlineUserService;
        this.tokenUtil = tokenUtil;
        this.authenticationManagerBuilder = authenticationManagerBuilder;
        this.yxUserService = yxUserService;
        this.passwordEncoder = passwordEncoder;
        this.userLoginService = userLoginService;
    }

    @Log("用户登录")
    @ApiOperation("登录授权")
    @AnonymousAccess
    @PostMapping(value = "/login")
    public ResponseEntity<Object> login(@Validated @RequestBody AuthUser authUser, HttpServletRequest request){
        // 密码解密
        RSA rsa = new RSA(privateKey, null);
        String password = new String(rsa.decrypt(authUser.getPassword(), KeyType.PrivateKey));
        // 查询验证码
        String code = (String) redisUtils.get(authUser.getUuid());
        // 清除验证码
        redisUtils.del(authUser.getUuid());
        if (StringUtils.isBlank(code)) {
            throw new BadRequestException("验证码不存在或已过期");
        }
        if (StringUtils.isBlank(authUser.getCode()) || !authUser.getCode().equalsIgnoreCase(code)) {
            throw new BadRequestException("验证码错误");
        }
        Map<String,Object> authInfo = checkLogin(authUser.getUsername(),password,request);
        return ResponseEntity.ok(authInfo);
    }
    /**
     * 短信登录
     * @param authUser 登录实体
     * @param request
     * @return
     */
    @Log("短信登录")
    @AnonymousAccess
    @PostMapping(value = "/smsLogin")
    public ResponseEntity<Object> smsLogin(@Validated @RequestBody SmsLoginBody authUser, HttpServletRequest request){
        String key = properties.getCodeKey() + authUser.getPhonenumber();
        // 查询验证码
        String code = (String) redisUtils.get(key);
        // 清除验证码
        redisUtils.del(key);
        if (StringUtils.isBlank(code)) {
            throw new BadRequestException("验证码不存在或已过期");
        }
        if (StringUtils.isBlank(authUser.getSmsCode()) || !authUser.getSmsCode().equalsIgnoreCase(code)) {
            throw new BadRequestException("验证码错误");
        }
        UserDetails user = userLoginService.loadUserByPhoneNumber(authUser.getPhonenumber());

        Map<String,Object> authInfo = checkLogin(user.getUsername(),user.getPassword(),request);
        return ResponseEntity.ok(authInfo);
    }

    @ApiOperation("获取用户信息")
    @GetMapping(value = "/info")
    public ResponseEntity<Object> getUserInfo(){
        JwtUser jwtUser = (JwtUser)userDetailsService.loadUserByUsername(SecurityUtils.getUsername());
        return ResponseEntity.ok(jwtUser);
    }

    @AnonymousAccess
    @ApiOperation("获取验证码")
    @GetMapping(value = "/code")
    public ResponseEntity<Object> getCode(){
        // 算术类型 https://gitee.com/whvse/EasyCaptcha
        ArithmeticCaptcha captcha = new ArithmeticCaptcha(111, 36);
        // 几位数运算，默认是两位
        captcha.setLen(2);
        // 获取运算的结果
        String result ="";
        try {
            result = new Double(Double.parseDouble(captcha.text())).intValue()+"";
        }catch (Exception e){
            result = captcha.text();
        }
        String uuid = properties.getCodeKey() + IdUtil.simpleUUID();
        // 保存
        redisUtils.set(uuid, result, expiration, TimeUnit.MINUTES);
        // 验证码信息
        Map<String,Object> imgResult = new HashMap<String,Object>(2){{
            put("img", captcha.toBase64());
            put("uuid", uuid);
        }};
        return ResponseEntity.ok(imgResult);
    }
    @AnonymousAccess
    @ApiOperation("获取短信验证码")
    @PostMapping(value = "/smsCode")
    public ResponseEntity<Object> getSmsCode(@Validated @RequestBody AuthSms authSms, HttpServletRequest request){
//        if (!checkCode(authSms.getUuid(),authSms.getCode())){
//            throw new BadRequestException("验证码错误");
//        }
        String key =  properties.getCodeKey() + authSms.getPhone();
        String smsCode = RandomUtil.randomNumbers(4);
        redisUtils.set(key, smsCode, smsExpiration,TimeUnit.MINUTES);
        // 验证码模板id 自行处理 (查数据库或写死均可)
        String templateId = "2032916";
        LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
        map.put("code", smsCode);
        SmsBlend smsBlend = SmsFactory.getSmsBlend("tx1");
        SmsResponse smsResponse = smsBlend.sendMessage(authSms.getPhone(), templateId, map);
        if (false == smsResponse.isSuccess()) {
            log.error("验证码短信发送异常 => {}", smsResponse);
            throw new BadRequestException("验证码错误");
        }
        return ResponseEntity.ok(HttpStatus.OK);
    }

    @Log("用户注册")
    @ApiOperation(value = "用户注册")
    @PutMapping(value = "/register")
    @AnonymousAccess
    public ResponseEntity register(@Validated @RequestBody RegisterUser resources){
        // 密码解密
        RSA rsa = new RSA(privateKey, null);
        String password = new String(rsa.decrypt(resources.getPassword(), KeyType.PrivateKey));
        // 查询验证码
        String code = (String) redisUtils.get(resources.getUuid());
        // 清除验证码
        redisUtils.del(resources.getUuid());
        if (StringUtils.isBlank(code)) {
            throw new BadRequestException("验证码不存在或已过期");
        }
        if (StringUtils.isBlank(resources.getCode()) || !resources.getCode().equalsIgnoreCase(code)) {
            throw new BadRequestException("验证码错误");
        }
        String username = resources.getUsername();
        String phone = resources.getPhone();
        Long spreadUid = resources.getSpreadUid();
        /**
         * userType
         * AppFromEnum.app.getValue
         */
        YxUser app = YxUser.builder().username(username).phone(phone).password(passwordEncoder.encode(password)).integral(new BigDecimal(0)).nowMoney(new BigDecimal(0)).level(0).status(1).userType("app").spreadUid(spreadUid).build();
        yxUserService.saveOrUpdate(app);
        return new ResponseEntity(HttpStatus.CREATED);
    }

    @ApiOperation("退出登录")
    @AnonymousAccess
    @DeleteMapping(value = "/logout")
    public ResponseEntity<Object> logout(HttpServletRequest request){
        onlineUserService.logout(tokenUtil.getToken(request));
        return new ResponseEntity<>(HttpStatus.OK);
    }


    private Map<String,Object> checkLogin(String userName,String password, HttpServletRequest request){
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(userName, password);

        Authentication authentication = authenticationManagerBuilder.authenticate(authenticationToken);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // 生成令牌
        final UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        String token = tokenUtil.generateToken(userDetails);
        final JwtUser jwtUser = (JwtUser) authentication.getPrincipal();
        // 保存在线信息
        onlineUserService.save(jwtUser, token, request);
        // 返回 token 与 用户信息
        Map<String,Object> authInfo = new HashMap<String,Object>(2){{
            put("token", properties.getTokenStartWith() + token);
            put("user", jwtUser);
        }};
        if(singleLogin){
            //踢掉之前已经登录的token
            onlineUserService.checkLoginOnUser(userName,token);
        }
        return authInfo;
    }

    private boolean checkCode(String uuid,String checkCode){
        // 查询验证码
        String code = (String) redisUtils.get(uuid);
        // 清除验证码
        redisUtils.del(uuid);
        if (StringUtils.isBlank(code)) {
            throw new BadRequestException("验证码不存在或已过期");
        }
        if (StringUtils.isBlank(checkCode) || !checkCode.equalsIgnoreCase(code)) {
            throw new BadRequestException("验证码错误");
        }
        return true;
    }

}
